package com.xxjqr.rbac.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageInfo;
import com.xxjqr.rbac.auth.DynamicSecurityMetadataSource;
import com.xxjqr.rbac.entity.pojo.TbMenu;
import com.xxjqr.rbac.entity.pojo.TbRole;
import com.xxjqr.rbac.entity.pojo.TbRoleMenuRel;
import com.xxjqr.rbac.entity.dto.RoleDto;
import com.xxjqr.rbac.entity.pojo.TbUserRoleRel;
import com.xxjqr.rbac.entity.vo.RoleVo;
import com.xxjqr.rbac.enums.CodeMsgEnum;
import com.xxjqr.rbac.exception.CommonException;
import com.xxjqr.rbac.mapper.TbMenuMapper;
import com.xxjqr.rbac.mapper.TbRoleMapper;
import com.xxjqr.rbac.mapper.TbRoleMenuRelMapper;
import com.xxjqr.rbac.mapper.TbUserRoleRelMapper;
import com.xxjqr.rbac.service.TbRoleMenuRelService;
import com.xxjqr.rbac.service.TbRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxjqr.rbac.util.SnowIdUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author 丁昌江
 * @since 2021-01-25
 */
@Service
@AllArgsConstructor
public class TbRoleServiceImpl extends ServiceImpl<TbRoleMapper, TbRole> implements TbRoleService {

    private final TbRoleMenuRelService roleMenuRelService;
    private final TbMenuMapper menuMapper;
    private final TbRoleMapper roleMapper;
    private final TbRoleMenuRelMapper roleMenuRelMapper;
    private final TbUserRoleRelMapper userRoleRelMapper;
    private DynamicSecurityMetadataSource securityMetadataSource;

    @Override
    public PageInfo<RoleVo> getRoles(String name, Integer status) {
        List<RoleVo> roleVos = roleMapper.selectRoleVoPage(name, status);
        return PageInfo.of(roleVos);
    }


    @Override
    public int addRole(RoleDto roleDto) {
        TbRole tbRole = new TbRole();
        BeanUtils.copyProperties(roleDto, tbRole);
        tbRole.setId(SnowIdUtil.nextId());

        List<TbRoleMenuRel> roleMenuRels = new ArrayList<>();
        for (Long menuId : roleDto.getMenuIds()) {
            TbRoleMenuRel tbRoleMenuRel = new TbRoleMenuRel();
            tbRoleMenuRel.setRoleId(tbRole.getId());
            tbRoleMenuRel.setMenuId(menuId);
            tbRoleMenuRel.setRoleCode(roleDto.getCode());
            tbRoleMenuRel.setRoleName(tbRole.getName());
            TbMenu tbMenu = menuMapper.selectById(menuId);
            if (tbMenu == null) {
                throw CommonException.generate(CodeMsgEnum.NOTFOUND);
            }
            tbRoleMenuRel.setMenuName(tbMenu.getName());
            tbRoleMenuRel.setId(SnowIdUtil.nextId());
            roleMenuRels.add(tbRoleMenuRel);
        }
        baseMapper.insert(tbRole);
        return roleMenuRelService.saveBatch(roleMenuRels) ? 1 : 0;
    }

    @Override
    public List<Long> getRoleMenuIds(Long id) {
        return roleMenuRelMapper.getRoleMenuIds(id);
    }

    @Override
    @Transactional
    public Boolean editRole(RoleDto roleDto) {

        //处理角色状态修改或者删除时，用户角色的绑定关系也要删除
        Long roleId = roleDto.getId();
        Integer roleStatus = roleDto.getStatus();
        if (roleStatus != null && roleStatus == 0) {
            TbRole existRole = roleMapper.selectById(roleId);
            if (!roleStatus.equals(existRole.getStatus())) {
                List<TbUserRoleRel> tbUserRoleRels = userRoleRelMapper.selectList(new LambdaQueryWrapper<TbUserRoleRel>()
                        .eq(TbUserRoleRel::getRoleId, roleId)
                        .eq(TbUserRoleRel::getIsDelete, 0));
                List<Long> ids = tbUserRoleRels.stream().map(TbUserRoleRel::getId).collect(Collectors.toList());
                userRoleRelMapper.deleteBatchIds(ids);
            }
        }

        TbRole tbRole = new TbRole();
        BeanUtils.copyProperties(roleDto, tbRole);
        boolean success = updateById(tbRole);

        Set<Long> menuIds = iterateMenuId(roleDto.getMenuIds());
        List<TbRoleMenuRel> tbRoleMenuRels = roleMenuRelMapper.selectList(new LambdaQueryWrapper<TbRoleMenuRel>().eq(TbRoleMenuRel::getRoleId, roleId));
        List<Long> existMenuIds = tbRoleMenuRels.stream().map(TbRoleMenuRel::getMenuId).collect(Collectors.toList());
        existMenuIds.removeAll(menuIds);
        if (existMenuIds.size() > 0) {
            securityMetadataSource.clearDataSource();
            roleMenuRelMapper.delete(new LambdaQueryWrapper<TbRoleMenuRel>().in(TbRoleMenuRel::getMenuId, existMenuIds));
        }
        if (!CollectionUtils.isEmpty(menuIds)) {

            for (Long menuId : menuIds) {
                Optional<TbRoleMenuRel> roleMenuRelOptional = tbRoleMenuRels
                        .stream()
                        .filter(roleMenu -> roleMenu.getMenuId().equals(menuId))
                        .findFirst();
                if (!roleMenuRelOptional.isPresent()) {
                    securityMetadataSource.clearDataSource();
                    TbMenu tbMenu = menuMapper.selectById(menuId);
                    if (tbMenu == null) {
                        throw CommonException.generate(CodeMsgEnum.NOTFOUND);
                    }
                    TbRoleMenuRel instance = TbRoleMenuRel.instance(roleId, menuId, roleDto.getName(), roleDto.getCode(), tbMenu.getName());
                    roleMenuRelMapper.insert(instance);
                }
            }
        }
        return success;
    }

    private Set<Long> iterateMenuId(List<Long> menuIds) {
        Set<Long> menuIdSet = new HashSet<>();
        for (Long menuId : menuIds) {
            TbMenu tbMenu = menuMapper.selectById(menuId);
            String path = tbMenu.getPath();
            String[] split = path.split(",");
            for (String s : split) {
                menuIdSet.add(Long.parseLong(s));
            }
            menuIdSet.add(menuId);
        }
        return menuIdSet;
    }

    @Override
    public List<RoleVo> getRoleList() {
        return roleMapper.getRoleList();
    }

    @Override
    public Boolean delRole(Long id) {

        List<TbUserRoleRel> tbUserRoleRels = userRoleRelMapper.selectList(new LambdaQueryWrapper<TbUserRoleRel>()
                .eq(TbUserRoleRel::getRoleId, id)
                .eq(TbUserRoleRel::getIsDelete, 0));
        userRoleRelMapper.deleteBatchIds(tbUserRoleRels);

        return removeById(id);

    }
}
